﻿using System;
using System.Collections.Generic;
using System.Linq;
using Fuse8.ContentManagementFramework.BizFacade.Workflow;
using Fuse8.ContentManagementFramework.Domain.Bases;
using Fuse8.DomainFramework.BusinessService;
using Fuse8.DomainFramework.Common;
using Fuse8.DomainFramework.Common.Locator;

using Fuse8.ContentManagementFramework.Domain;
using Fuse8.ContentManagementFramework.Domain.Special;
using Fuse8.ContentManagementFramework.Domain.Commands;
using Fuse8.ContentManagementFramework.Domain.Commands.CommandParameters;

namespace Fuse8.ContentManagementFramework.BizFacade.Services
{
    public interface ICmsEntityRevisionService
    {
        void StartWorkflow(int entityId, DateTime? approvalDeadlineDate, DateTime? reviewDeadlineDate);
        void Remove(int entityId);
        void Withdraw(int entityId);

        IEnumerable<CmsRevisionableEntityObject> FindAllDraftRevisions(SiteAwareCategory siteAwareCategory = SiteAwareCategory.Site);
        IEnumerable<CmsRevisionableEntityObject> FindAllActiveRevisions(SiteAwareCategory siteAwareCategory = SiteAwareCategory.Site);
        IEnumerable<CmsRevisionableEntityObject> FindAllApprovedRevisions(SiteAwareCategory siteAwareCategory = SiteAwareCategory.Site);
    }

    public abstract class CmsEntityRevisionService<T> : CmsSiteBusinessService<T>, ICmsEntityRevisionService where T : CmsRevisionableEntityObject, new()
    {
        public abstract IEnumerable<T> FindAllRevisions(T currentEntity);

        public abstract IEnumerable<T> FindAllDraftRevisions(SiteAwareCategory siteAwareCategory = SiteAwareCategory.Site);

        public abstract IEnumerable<T> FindAllActiveRevisions(SiteAwareCategory siteAwareCategory = SiteAwareCategory.Site);

        public abstract IEnumerable<T> FindAllApprovedRevisions(SiteAwareCategory siteAwareCategory = SiteAwareCategory.Site);


        public abstract T GetCurrentRevision(int entityId);

        public virtual void UpdateCurrentRevision(T currentEntity)
        {
            Guard.ArgumentNotNull(currentEntity, "currentEntity");

            var command = DependencyServiceLocator.Current.GetInstance<CmsEntityRevisionUpdateCommand<T>>();

            var commandParameter = new CmsEntityRevisionUpdateCommandParameter
            {
                EntityId = currentEntity.Id,
                EntityTypeParameter = currentEntity.TypeParameter,
                IsLatestRevision = currentEntity.IsLatestRevision,
                IsLatestCompletedRevision = currentEntity.IsLatestCompletedRevision,
                WorkflowId = currentEntity.WorkflowId
            };

            command.ConfigureCommand(commandParameter);

            Execute(command);
        }


        public virtual void StartWorkflow(T entity, DateTime? approvalDeadlineDate, DateTime? reviewDeadlineDate)
        {
            int transitionId;
            using (var provider = new CmsWorkflowFacade<T, int>(entity))
            {
                transitionId = provider.GetAvailableTransitions().Any() ? provider.GetAvailableTransitions().First().Key : 0;
            }
            StartWorkflow(entity, approvalDeadlineDate, reviewDeadlineDate, transitionId, string.Empty);
        }

        public virtual void StartWorkflow(T entity, DateTime? approvalDeadlineDate, DateTime? reviewDeadlineDate, int transitionId, string notes, int? assignedUserId = null)
        {
            Guard.ArgumentNotNull(entity, "entity");

            using (var provider = new CmsWorkflowFacade<T, int>(entity))
            {
                provider.Create(approvalDeadlineDate, reviewDeadlineDate);

                if (transitionId > 0)
                {
                    provider.MakeTransition(transitionId, notes, assignedUserId);
                }
            }

            UpdateCurrentRevision(entity);            
        }

        public virtual void ProcessWorkflow(T entity, string notes, int transitionId = 0, int? assignedUserId = null)
        {
            Guard.ArgumentNotNull(entity, "entity");

            using (var provider = new CmsWorkflowFacade<T, int>(entity))
            {
                if (transitionId > 0)
                {
                    provider.MakeTransition(transitionId, notes, assignedUserId);
                }
            }

            if (transitionId > 0)
            {
                UpdateCurrentRevision(entity);
            }
        }

        #region ICmsEntityRevisionService

        public virtual void StartWorkflow(int entityId, DateTime? approvalDeadlineDate, DateTime? reviewDeadlineDate)
        {
            var entity = FindOne(entityId);
            if (entity != null && entity.WorkflowId == null)
            {
                StartWorkflow(entity, approvalDeadlineDate, reviewDeadlineDate);
            }
        }

        public virtual void Withdraw(int entityId)
        {
            var entity = FindOne(entityId);
            if (entity != null)
            {
                using (var provider = new CmsWorkflowFacade<T, int>(entity))
                {
                    provider.Withdraw();
                }

                UpdateCurrentRevision(entity);  
            }
        }


        IEnumerable<CmsRevisionableEntityObject> ICmsEntityRevisionService.FindAllActiveRevisions(SiteAwareCategory siteAwareCategory)
        {
            return FindAllActiveRevisions(siteAwareCategory);
        }

        IEnumerable<CmsRevisionableEntityObject> ICmsEntityRevisionService.FindAllDraftRevisions(SiteAwareCategory siteAwareCategory)
        {
            return FindAllDraftRevisions(siteAwareCategory);
        }

        IEnumerable<CmsRevisionableEntityObject> ICmsEntityRevisionService.FindAllApprovedRevisions(SiteAwareCategory siteAwareCategory)
        {
            return FindAllApprovedRevisions(siteAwareCategory);
        }

        #endregion


    }
}
